{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "76918dda",
   "metadata": {
    "origin_pos": 0,
    "id": "76918dda"
   },
   "source": [
    "# 数据操作\n",
    ":label:`sec_ndarray`\n",
    "\n",
    "为了能够完成各种数据操作，我们需要某种方法来存储和操作数据。\n",
    "通常，我们需要做两件重要的事：（1）获取数据；（2）将数据读入计算机后对其进行处理。\n",
    "如果没有某种方法来存储数据，那么获取数据是没有意义的。\n",
    "\n",
    "首先，我们介绍$n$维数组，也称为*张量*（tensor）。\n",
    "使用过Python中NumPy计算包的读者会对本部分很熟悉。\n",
    "无论使用哪个深度学习框架，它的*张量类*（在MXNet中为`ndarray`，\n",
    "在PyTorch和TensorFlow中为`Tensor`）都与Numpy的`ndarray`类似。\n",
    "但深度学习框架又比Numpy的`ndarray`多一些重要功能：\n",
    "首先，GPU很好地支持加速计算，而NumPy仅支持CPU计算；\n",
    "其次，张量类支持自动微分。\n",
    "这些功能使得张量类更适合深度学习。\n",
    "如果没有特殊说明，本书中所说的张量均指的是张量类的实例。\n",
    "\n",
    "## 入门\n",
    "\n",
    "本节的目标是帮助读者了解并运行一些在阅读本书的过程中会用到的基本数值计算工具。\n",
    "如果你很难理解一些数学概念或库函数，请不要担心。\n",
    "后面的章节将通过一些实际的例子来回顾这些内容。\n",
    "如果你已经具有相关经验，想要深入学习数学内容，可以跳过本节。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26a77426",
   "metadata": {
    "origin_pos": 2,
    "tab": [
     "pytorch"
    ],
    "id": "26a77426"
   },
   "source": [
    "(**首先，我们导入`torch`。请注意，虽然它被称为PyTorch，但是代码中使用`torch`而不是`pytorch`。**)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f05fbe34",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:13.962912Z",
     "iopub.status.busy": "2022-07-31T02:18:13.962445Z",
     "iopub.status.idle": "2022-07-31T02:18:14.683806Z",
     "shell.execute_reply": "2022-07-31T02:18:14.683047Z"
    },
    "origin_pos": 5,
    "tab": [
     "pytorch"
    ],
    "id": "f05fbe34"
   },
   "outputs": [],
   "source": [
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "[**张量表示由一个数值组成的数组，这个数组可能有多个维度**]。\n",
    "具有一个轴的张量对应数学上的*向量*（vector）；\n",
    "具有两个轴的张量对应数学上的*矩阵*（matrix）；\n",
    "具有两个轴以上的张量没有特殊的数学名称。\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "b13f33923093f675"
  },
  {
   "cell_type": "markdown",
   "id": "82e1aab1",
   "metadata": {
    "origin_pos": 9,
    "tab": [
     "pytorch"
    ],
    "id": "82e1aab1"
   },
   "source": [
    "首先，我们可以使用 `arange` 创建一个行向量 `x`。这个行向量包含以0开始的前12个整数，它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的 *元素*（element）。例如，张量 `x` 中有 12 个元素。除非额外指定，新的张量将存储在内存中，并采用基于CPU的计算。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0772c3dc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.687487Z",
     "iopub.status.busy": "2022-07-31T02:18:14.687092Z",
     "iopub.status.idle": "2022-07-31T02:18:14.698472Z",
     "shell.execute_reply": "2022-07-31T02:18:14.697874Z"
    },
    "origin_pos": 12,
    "tab": [
     "pytorch"
    ],
    "id": "0772c3dc",
    "outputId": "dc96381a-e2de-4e14-c727-21c80bfe21ae"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.arange(12)\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6fc166f",
   "metadata": {
    "origin_pos": 14,
    "id": "b6fc166f"
   },
   "source": [
    "[**可以通过张量的`shape`属性来访问张量（沿每个轴的长度）的*形状***]\n",
    "(~~和张量中元素的总数~~)。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82eeca50",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.703433Z",
     "iopub.status.busy": "2022-07-31T02:18:14.703051Z",
     "iopub.status.idle": "2022-07-31T02:18:14.708075Z",
     "shell.execute_reply": "2022-07-31T02:18:14.707270Z"
    },
    "origin_pos": 15,
    "tab": [
     "pytorch"
    ],
    "id": "82eeca50",
    "outputId": "17730ea7-a06a-4d02-caa0-ff86bfc84e2e"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([12])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fdea867",
   "metadata": {
    "origin_pos": 16,
    "id": "5fdea867"
   },
   "source": [
    "如果只想知道张量中元素的总数，即形状的所有元素乘积，可以检查它的大小（size）。\n",
    "因为这里在处理的是一个向量，所以它的`shape`与它的`size`相同。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0e043a9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.711683Z",
     "iopub.status.busy": "2022-07-31T02:18:14.710908Z",
     "iopub.status.idle": "2022-07-31T02:18:14.716724Z",
     "shell.execute_reply": "2022-07-31T02:18:14.715786Z"
    },
    "origin_pos": 18,
    "tab": [
     "pytorch"
    ],
    "id": "d0e043a9",
    "outputId": "bb8e79f1-3a02-4850-aba1-90f2b2707363"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.numel()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f10259c",
   "metadata": {
    "origin_pos": 20,
    "id": "9f10259c"
   },
   "source": [
    "[**要想改变一个张量的形状而不改变元素数量和元素值，可以调用`reshape`函数。**]\n",
    "例如，可以把张量`x`从形状为（12,）的行向量转换为形状为（3,4）的矩阵。\n",
    "这个新的张量包含与转换前相同的值，但是它被看成一个3行4列的矩阵。\n",
    "要重点说明一下，虽然张量的形状发生了改变，但其元素值并没有变。\n",
    "注意，通过改变张量的形状，张量的大小不会改变。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a0f1946",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.719843Z",
     "iopub.status.busy": "2022-07-31T02:18:14.719478Z",
     "iopub.status.idle": "2022-07-31T02:18:14.726230Z",
     "shell.execute_reply": "2022-07-31T02:18:14.725362Z"
    },
    "origin_pos": 21,
    "tab": [
     "pytorch"
    ],
    "id": "4a0f1946",
    "outputId": "342c2bf6-ebaf-4f85-a09d-8a4afde4da7b"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7],\n",
       "        [ 8,  9, 10, 11]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = x.reshape(3, 4)\n",
    "X"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5ea0669",
   "metadata": {
    "origin_pos": 23,
    "id": "f5ea0669"
   },
   "source": [
    "我们不需要通过手动指定每个维度来改变形状。\n",
    "也就是说，如果我们的目标形状是（高度,宽度），\n",
    "那么在知道宽度后，高度会被自动计算得出，不必我们自己做除法。\n",
    "在上面的例子中，为了获得一个3行的矩阵，我们手动指定了它有3行和4列。\n",
    "幸运的是，我们可以通过`-1`来调用此自动计算出维度的功能。\n",
    "即我们可以用`x.reshape(-1,4)`或`x.reshape(3,-1)`来取代`x.reshape(3,4)`。\n",
    "\n",
    "有时，我们希望[**使用全0、全1、其他常量，或者从特定分布中随机采样的数字**]来初始化矩阵。\n",
    "我们可以创建一个形状为（2,3,4）的张量，其中所有元素都设置为0。代码如下：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81129c4f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.730172Z",
     "iopub.status.busy": "2022-07-31T02:18:14.729443Z",
     "iopub.status.idle": "2022-07-31T02:18:14.736998Z",
     "shell.execute_reply": "2022-07-31T02:18:14.736078Z"
    },
    "origin_pos": 25,
    "tab": [
     "pytorch"
    ],
    "id": "81129c4f",
    "outputId": "0f9312f8-d388-4356-8b53-0f2c8922f118"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[0., 0., 0., 0.],\n",
       "         [0., 0., 0., 0.],\n",
       "         [0., 0., 0., 0.]],\n",
       "\n",
       "        [[0., 0., 0., 0.],\n",
       "         [0., 0., 0., 0.],\n",
       "         [0., 0., 0., 0.]]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.zeros((2, 3, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29efa60f",
   "metadata": {
    "origin_pos": 27,
    "id": "29efa60f"
   },
   "source": [
    "同样，我们可以创建一个形状为`(2,3,4)`的张量，其中所有元素都设置为1。代码如下：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b67243de",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.741124Z",
     "iopub.status.busy": "2022-07-31T02:18:14.740532Z",
     "iopub.status.idle": "2022-07-31T02:18:14.748021Z",
     "shell.execute_reply": "2022-07-31T02:18:14.747164Z"
    },
    "origin_pos": 29,
    "tab": [
     "pytorch"
    ],
    "id": "b67243de",
    "outputId": "7081851b-af07-4649-aab7-9de6086cf80a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[1., 1., 1., 1.],\n",
       "         [1., 1., 1., 1.],\n",
       "         [1., 1., 1., 1.]],\n",
       "\n",
       "        [[1., 1., 1., 1.],\n",
       "         [1., 1., 1., 1.],\n",
       "         [1., 1., 1., 1.]]])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.ones((2, 3, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65af1bf6",
   "metadata": {
    "origin_pos": 31,
    "id": "65af1bf6"
   },
   "source": [
    "有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。\n",
    "例如，当我们构造数组来作为神经网络中的参数时，我们通常会随机初始化参数的值。\n",
    "以下代码创建一个形状为（3,4）的张量。\n",
    "其中的每个元素都从均值为0、标准差为1的标准高斯分布（正态分布）中随机采样。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5227275f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.751936Z",
     "iopub.status.busy": "2022-07-31T02:18:14.751150Z",
     "iopub.status.idle": "2022-07-31T02:18:14.758391Z",
     "shell.execute_reply": "2022-07-31T02:18:14.757602Z"
    },
    "origin_pos": 33,
    "tab": [
     "pytorch"
    ],
    "id": "5227275f",
    "outputId": "6aafaaf4-2186-46e0-efeb-74a151c2c0ed"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-0.5582, -0.0443,  1.6146,  0.6003],\n",
       "        [-1.7652,  1.3074,  0.5233,  1.4372],\n",
       "        [ 0.2452,  2.2281,  1.3483,  0.1783]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.randn(3, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4173078",
   "metadata": {
    "origin_pos": 35,
    "id": "f4173078"
   },
   "source": [
    "我们还可以[**通过提供包含数值的Python列表（或嵌套列表），来为所需张量中的每个元素赋予确定值**]。\n",
    "在这里，最外层的列表对应于轴0，内层的列表对应于轴1。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06f989db",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.762256Z",
     "iopub.status.busy": "2022-07-31T02:18:14.761498Z",
     "iopub.status.idle": "2022-07-31T02:18:14.769515Z",
     "shell.execute_reply": "2022-07-31T02:18:14.768586Z"
    },
    "origin_pos": 37,
    "tab": [
     "pytorch"
    ],
    "id": "06f989db",
    "outputId": "b9e32bfd-d55a-4618-a8b6-2b091964f07e"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[2, 1, 4, 3],\n",
       "        [1, 2, 3, 4],\n",
       "        [4, 3, 2, 1]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f23a11a6",
   "metadata": {
    "origin_pos": 39,
    "id": "f23a11a6"
   },
   "source": [
    "## 运算符\n",
    "\n",
    "我们的兴趣不仅限于读取数据和写入数据。\n",
    "我们想在这些数据上执行数学运算，其中最简单且最有用的操作是*按元素*（elementwise）运算。\n",
    "它们将标准标量运算符应用于数组的每个元素。\n",
    "对于将两个数组作为输入的函数，按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。\n",
    "我们可以基于任何从标量到标量的函数来创建按元素函数。\n",
    "\n",
    "在数学表示法中，我们将通过符号$f: \\mathbb{R} \\rightarrow \\mathbb{R}$\n",
    "来表示*一元*标量运算符（只接收一个输入）。\n",
    "这意味着该函数从任何实数（$\\mathbb{R}$）映射到另一个实数。\n",
    "同样，我们通过符号$f: \\mathbb{R}, \\mathbb{R} \\rightarrow \\mathbb{R}$\n",
    "表示*二元*标量运算符，这意味着该函数接收两个输入，并产生一个输出。\n",
    "给定同一形状的任意两个向量$\\mathbf{u}$和$\\mathbf{v}$和二元运算符$f$，\n",
    "我们可以得到向量$\\mathbf{c} = F(\\mathbf{u},\\mathbf{v})$。\n",
    "具体计算方法是$c_i \\gets f(u_i, v_i)$，\n",
    "其中$c_i$、$u_i$和$v_i$分别是向量$\\mathbf{c}$、$\\mathbf{u}$和$\\mathbf{v}$中的元素。\n",
    "在这里，我们通过将标量函数升级为按元素向量运算来生成向量值\n",
    "$F: \\mathbb{R}^d, \\mathbb{R}^d \\rightarrow \\mathbb{R}^d$。\n",
    "\n",
    "对于任意具有相同形状的张量，\n",
    "[**常见的标准算术运算符（`+`、`-`、`*`、`/`和`**`）都可以被升级为按元素运算**]。\n",
    "我们可以在同一形状的任意两个张量上调用按元素操作。\n",
    "在下面的例子中，我们使用逗号来表示一个具有5个元素的元组，其中每个元素都是按元素操作的结果。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c350a6ca",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.773563Z",
     "iopub.status.busy": "2022-07-31T02:18:14.772808Z",
     "iopub.status.idle": "2022-07-31T02:18:14.784552Z",
     "shell.execute_reply": "2022-07-31T02:18:14.783458Z"
    },
    "origin_pos": 41,
    "tab": [
     "pytorch"
    ],
    "id": "c350a6ca",
    "outputId": "488052ba-3006-484b-f875-d4b7e4b2fbe4"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([ 3.,  4.,  6., 10.]),\n",
       " tensor([-1.,  0.,  2.,  6.]),\n",
       " tensor([ 2.,  4.,  8., 16.]),\n",
       " tensor([0.5000, 1.0000, 2.0000, 4.0000]),\n",
       " tensor([ 1.,  4., 16., 64.]))"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.tensor([1.0, 2, 4, 8])\n",
    "y = torch.tensor([2, 2, 2, 2])\n",
    "x + y, x - y, x * y, x / y, x ** y  # **运算符是求幂运算"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4068b5b8",
   "metadata": {
    "origin_pos": 43,
    "id": "4068b5b8"
   },
   "source": [
    "(**“按元素”方式可以应用更多的计算**)，包括像求幂这样的一元运算符。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4550d12",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.788732Z",
     "iopub.status.busy": "2022-07-31T02:18:14.788133Z",
     "iopub.status.idle": "2022-07-31T02:18:14.796104Z",
     "shell.execute_reply": "2022-07-31T02:18:14.794959Z"
    },
    "origin_pos": 45,
    "tab": [
     "pytorch"
    ],
    "id": "e4550d12",
    "outputId": "f18bace8-bbe4-4fde-f7e0-615271d985dc"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.exp(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "305cc670",
   "metadata": {
    "origin_pos": 47,
    "id": "305cc670"
   },
   "source": [
    "除了按元素计算外，我们还可以执行线性代数运算，包括向量点积和矩阵乘法。\n",
    "我们将在 :numref:`sec_linear-algebra`中解释线性代数的重点内容。\n",
    "\n",
    "[**我们也可以把多个张量*连结*（concatenate）在一起**]，\n",
    "把它们端对端地叠起来形成一个更大的张量。\n",
    "我们只需要提供张量列表，并给出沿哪个轴连结。\n",
    "下面的例子分别演示了当我们沿行（轴-0，形状的第一个元素）\n",
    "和按列（轴-1，形状的第二个元素）连结两个矩阵时，会发生什么情况。\n",
    "我们可以看到，第一个输出张量的轴-0长度（$6$）是两个输入张量轴-0长度的总和（$3 + 3$）；\n",
    "第二个输出张量的轴-1长度（$8$）是两个输入张量轴-1长度的总和（$4 + 4$）。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60fce770",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.799639Z",
     "iopub.status.busy": "2022-07-31T02:18:14.799002Z",
     "iopub.status.idle": "2022-07-31T02:18:14.810588Z",
     "shell.execute_reply": "2022-07-31T02:18:14.809683Z"
    },
    "origin_pos": 49,
    "tab": [
     "pytorch"
    ],
    "id": "60fce770",
    "outputId": "cb4ed97e-b8ea-4795-fe18-7f1caf530beb"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[ 0.,  1.,  2.,  3.],\n",
       "         [ 4.,  5.,  6.,  7.],\n",
       "         [ 8.,  9., 10., 11.],\n",
       "         [ 2.,  1.,  4.,  3.],\n",
       "         [ 1.,  2.,  3.,  4.],\n",
       "         [ 4.,  3.,  2.,  1.]]),\n",
       " tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],\n",
       "         [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],\n",
       "         [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = torch.arange(12, dtype=torch.float32).reshape((3,4))\n",
    "Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])\n",
    "torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75a17611",
   "metadata": {
    "origin_pos": 51,
    "id": "75a17611"
   },
   "source": [
    "有时，我们想[**通过*逻辑运算符*构建二元张量**]。\n",
    "以`X == Y`为例：\n",
    "对于每个位置，如果`X`和`Y`在该位置相等，则新张量中相应项的值为1。\n",
    "这意味着逻辑语句`X == Y`在该位置处为真，否则该位置为0。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16aff702",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.813799Z",
     "iopub.status.busy": "2022-07-31T02:18:14.813591Z",
     "iopub.status.idle": "2022-07-31T02:18:14.818445Z",
     "shell.execute_reply": "2022-07-31T02:18:14.817794Z"
    },
    "origin_pos": 52,
    "tab": [
     "pytorch"
    ],
    "id": "16aff702",
    "outputId": "25479981-dc4b-42ed-d1f6-9fbd94158883"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[False,  True, False,  True],\n",
       "        [False, False, False, False],\n",
       "        [False, False, False, False]])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X == Y"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4727a92b",
   "metadata": {
    "origin_pos": 53,
    "id": "4727a92b"
   },
   "source": [
    "[**对张量中的所有元素进行求和，会产生一个单元素张量。**]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2929b13d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.821528Z",
     "iopub.status.busy": "2022-07-31T02:18:14.821076Z",
     "iopub.status.idle": "2022-07-31T02:18:14.825960Z",
     "shell.execute_reply": "2022-07-31T02:18:14.825290Z"
    },
    "origin_pos": 54,
    "tab": [
     "pytorch"
    ],
    "id": "2929b13d",
    "outputId": "d4e91dc7-8c8f-4edc-88de-10876f2677ba"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(66.)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X.sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82d37b7e",
   "metadata": {
    "origin_pos": 56,
    "id": "82d37b7e"
   },
   "source": [
    "## 广播机制\n",
    ":label:`subsec_broadcasting`\n",
    "\n",
    "在上面的部分中，我们看到了如何在相同形状的两个张量上执行按元素操作。\n",
    "在某些情况下，[**即使形状不同，我们仍然可以通过调用\n",
    "*广播机制*（broadcasting mechanism）来执行按元素操作**]。\n",
    "这种机制的工作方式如下：首先，通过适当复制元素来扩展一个或两个数组，\n",
    "以便在转换之后，两个张量具有相同的形状。\n",
    "其次，对生成的数组执行按元素操作。\n",
    "\n",
    "在大多数情况下，我们将沿着数组中长度为1的轴进行广播，如下例子：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e6a50db",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.828845Z",
     "iopub.status.busy": "2022-07-31T02:18:14.828629Z",
     "iopub.status.idle": "2022-07-31T02:18:14.835266Z",
     "shell.execute_reply": "2022-07-31T02:18:14.834338Z"
    },
    "origin_pos": 58,
    "tab": [
     "pytorch"
    ],
    "id": "8e6a50db",
    "outputId": "7263f3af-33d8-43e6-a479-1a48e9cdb14f"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[0],\n",
       "         [1],\n",
       "         [2]]),\n",
       " tensor([[0, 1]]))"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = torch.arange(3).reshape((3, 1))\n",
    "b = torch.arange(2).reshape((1, 2))\n",
    "a, b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2af988b",
   "metadata": {
    "origin_pos": 60,
    "id": "c2af988b"
   },
   "source": [
    "由于`a`和`b`分别是$3\\times1$和$1\\times2$矩阵，如果让它们相加，它们的形状不匹配。\n",
    "我们将两个矩阵*广播*为一个更大的$3\\times2$矩阵，如下所示：矩阵`a`将复制列，\n",
    "矩阵`b`将复制行，然后再按元素相加。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f97353c7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.839218Z",
     "iopub.status.busy": "2022-07-31T02:18:14.838457Z",
     "iopub.status.idle": "2022-07-31T02:18:14.845915Z",
     "shell.execute_reply": "2022-07-31T02:18:14.844907Z"
    },
    "origin_pos": 61,
    "tab": [
     "pytorch"
    ],
    "id": "f97353c7",
    "outputId": "8ebe8a21-624d-4f1c-b2f8-7f97d9e3dc96"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0, 1],\n",
       "        [1, 2],\n",
       "        [2, 3]])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a + b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d822c22",
   "metadata": {
    "origin_pos": 62,
    "id": "3d822c22"
   },
   "source": [
    "## 索引和切片\n",
    "\n",
    "就像在任何其他Python数组中一样，张量中的元素可以通过索引访问。\n",
    "与任何Python数组一样：第一个元素的索引是0，最后一个元素索引是-1；\n",
    "可以指定范围以包含第一个元素和最后一个之前的元素。\n",
    "\n",
    "如下所示，我们[**可以用`[-1]`选择最后一个元素，可以用`[1:3]`选择第二个和第三个元素**]：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fbbcf30",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.849759Z",
     "iopub.status.busy": "2022-07-31T02:18:14.849160Z",
     "iopub.status.idle": "2022-07-31T02:18:14.856410Z",
     "shell.execute_reply": "2022-07-31T02:18:14.855540Z"
    },
    "origin_pos": 63,
    "tab": [
     "pytorch"
    ],
    "id": "4fbbcf30",
    "outputId": "b58f93df-43fe-4a78-a221-bb4b44109ae1"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([ 8.,  9., 10., 11.]),\n",
       " tensor([[ 4.,  5.,  6.,  7.],\n",
       "         [ 8.,  9., 10., 11.]]))"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X[-1], X[1:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64e0deb4",
   "metadata": {
    "origin_pos": 64,
    "tab": [
     "pytorch"
    ],
    "id": "64e0deb4"
   },
   "source": [
    "[**除读取外，我们还可以通过指定索引来将元素写入矩阵。**]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93a620d1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.860493Z",
     "iopub.status.busy": "2022-07-31T02:18:14.859755Z",
     "iopub.status.idle": "2022-07-31T02:18:14.866298Z",
     "shell.execute_reply": "2022-07-31T02:18:14.865434Z"
    },
    "origin_pos": 66,
    "tab": [
     "pytorch"
    ],
    "id": "93a620d1",
    "outputId": "2f91541e-bae1-4977-9af4-36f27a1d113c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0.,  1.,  2.,  3.],\n",
       "        [ 4.,  5.,  9.,  7.],\n",
       "        [ 8.,  9., 10., 11.]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X[1, 2] = 9\n",
    "X"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3400e726",
   "metadata": {
    "origin_pos": 68,
    "id": "3400e726"
   },
   "source": [
    "如果我们想[**为多个元素赋值相同的值，我们只需要索引所有元素，然后为它们赋值。**]\n",
    "例如，`[0:2, :]`访问第1行和第2行，其中“:”代表沿轴1（列）的所有元素。\n",
    "虽然我们讨论的是矩阵的索引，但这也适用于向量和超过2个维度的张量。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35bd3458",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.869487Z",
     "iopub.status.busy": "2022-07-31T02:18:14.868918Z",
     "iopub.status.idle": "2022-07-31T02:18:14.875244Z",
     "shell.execute_reply": "2022-07-31T02:18:14.874325Z"
    },
    "origin_pos": 69,
    "tab": [
     "pytorch"
    ],
    "id": "35bd3458",
    "outputId": "e75422ed-c577-48bb-ffb2-b430b060a81c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[12., 12., 12., 12.],\n",
       "        [12., 12., 12., 12.],\n",
       "        [ 8.,  9., 10., 11.]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X[0:2, :] = 12\n",
    "X"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4769a410",
   "metadata": {
    "origin_pos": 71,
    "id": "4769a410"
   },
   "source": [
    "## 节省内存\n",
    "\n",
    "[**运行一些操作可能会导致为新结果分配内存**]。\n",
    "例如，如果我们用`Y = X + Y`，我们将取消引用`Y`指向的张量，而是指向新分配的内存处的张量。\n",
    "\n",
    "在下面的例子中，我们用Python的`id()`函数演示了这一点，\n",
    "它给我们提供了内存中引用对象的确切地址。\n",
    "运行`Y = Y + X`后，我们会发现`id(Y)`指向另一个位置。\n",
    "这是因为Python首先计算`Y + X`，为结果分配新的内存，然后使`Y`指向内存中的这个新位置。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd3cfd4b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.878510Z",
     "iopub.status.busy": "2022-07-31T02:18:14.878253Z",
     "iopub.status.idle": "2022-07-31T02:18:14.883781Z",
     "shell.execute_reply": "2022-07-31T02:18:14.882922Z"
    },
    "origin_pos": 72,
    "tab": [
     "pytorch"
    ],
    "id": "cd3cfd4b",
    "outputId": "a2580a40-7e02-46e9-b035-ba79ea277f02"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "before = id(Y)\n",
    "Y = Y + X\n",
    "id(Y) == before"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f292984",
   "metadata": {
    "origin_pos": 73,
    "id": "3f292984"
   },
   "source": [
    "这可能是不可取的，原因有两个：首先，我们不想总是不必要地分配内存。\n",
    "在机器学习中，我们可能有数百兆的参数，并且在一秒内多次更新所有参数。\n",
    "通常情况下，我们希望原地执行这些更新。\n",
    "其次，如果我们不原地更新，其他引用仍然会指向旧的内存位置，\n",
    "这样我们的某些代码可能会无意中引用旧的参数。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84afb52c",
   "metadata": {
    "origin_pos": 74,
    "tab": [
     "pytorch"
    ],
    "id": "84afb52c"
   },
   "source": [
    "幸运的是，(**执行原地操作**)非常简单。\n",
    "我们可以使用切片表示法将操作的结果分配给先前分配的数组，例如`Y[:] = <expression>`。\n",
    "为了说明这一点，我们首先创建一个新的矩阵`Z`，其形状与另一个`Y`相同，\n",
    "使用`zeros_like`来分配一个全$0$的块。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a8d07ca",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.886922Z",
     "iopub.status.busy": "2022-07-31T02:18:14.886668Z",
     "iopub.status.idle": "2022-07-31T02:18:14.892130Z",
     "shell.execute_reply": "2022-07-31T02:18:14.891483Z"
    },
    "origin_pos": 77,
    "tab": [
     "pytorch"
    ],
    "id": "0a8d07ca",
    "outputId": "8cc7cc40-12bc-4961-a4fe-9e7aa24cfd3a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id(Z): 140316199714544\n",
      "id(Z): 140316199714544\n"
     ]
    }
   ],
   "source": [
    "Z = torch.zeros_like(Y)\n",
    "print('id(Z):', id(Z))\n",
    "Z[:] = X + Y\n",
    "print('id(Z):', id(Z))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bb4300d",
   "metadata": {
    "origin_pos": 79,
    "tab": [
     "pytorch"
    ],
    "id": "2bb4300d"
   },
   "source": [
    "[**如果在后续计算中没有重复使用`X`，\n",
    "我们也可以使用`X[:] = X + Y`或`X += Y`来减少操作的内存开销。**]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbe3b772",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.895112Z",
     "iopub.status.busy": "2022-07-31T02:18:14.894585Z",
     "iopub.status.idle": "2022-07-31T02:18:14.900298Z",
     "shell.execute_reply": "2022-07-31T02:18:14.899621Z"
    },
    "origin_pos": 81,
    "tab": [
     "pytorch"
    ],
    "id": "dbe3b772",
    "outputId": "7fa7df66-af18-4ddb-fdab-201e227af67d"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "before = id(X)\n",
    "X += Y\n",
    "id(X) == before"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd920d15",
   "metadata": {
    "origin_pos": 83,
    "id": "dd920d15"
   },
   "source": [
    "## 转换为其他Python对象\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c49606e1",
   "metadata": {
    "origin_pos": 85,
    "tab": [
     "pytorch"
    ],
    "id": "c49606e1"
   },
   "source": [
    "将深度学习框架定义的张量[**转换为NumPy张量（`ndarray`）**]很容易，反之也同样容易。\n",
    "torch张量和numpy数组将共享它们的底层内存，就地操作更改一个张量也会同时更改另一个张量。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d3d6ca45",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.903817Z",
     "iopub.status.busy": "2022-07-31T02:18:14.903370Z",
     "iopub.status.idle": "2022-07-31T02:18:14.910321Z",
     "shell.execute_reply": "2022-07-31T02:18:14.909306Z"
    },
    "origin_pos": 87,
    "tab": [
     "pytorch"
    ],
    "id": "d3d6ca45",
    "outputId": "7487c358-186a-47d8-8db7-db1c151b6161"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(numpy.ndarray, torch.Tensor)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = X.numpy()\n",
    "B = torch.tensor(A)\n",
    "type(A), type(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff8f24f4",
   "metadata": {
    "origin_pos": 89,
    "id": "ff8f24f4"
   },
   "source": [
    "要(**将大小为1的张量转换为Python标量**)，我们可以调用`item`函数或Python的内置函数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1c47523",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:18:14.913427Z",
     "iopub.status.busy": "2022-07-31T02:18:14.912724Z",
     "iopub.status.idle": "2022-07-31T02:18:14.920422Z",
     "shell.execute_reply": "2022-07-31T02:18:14.919387Z"
    },
    "origin_pos": 91,
    "tab": [
     "pytorch"
    ],
    "id": "a1c47523",
    "outputId": "8f1c7ea5-75c8-4448-d71f-44b2ade230b5"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([3.5000]), 3.5, 3.5, 3)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = torch.tensor([3.5])\n",
    "a, a.item(), float(a), int(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b1466e5",
   "metadata": {
    "origin_pos": 93,
    "id": "2b1466e5"
   },
   "source": [
    "## 小结\n",
    "\n",
    "* 深度学习存储和操作数据的主要接口是张量（$n$维数组）。它提供了各种功能，包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象。\n",
    "\n",
    "## 练习\n",
    "\n",
    "1. 运行本节中的代码。将本节中的条件语句`X == Y`更改为`X < Y`或`X > Y`，然后看看你可以得到什么样的张量。\n",
    "1. 用其他形状（例如三维张量）替换广播机制中按元素操作的两个张量。结果是否与预期相同？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 0,
    "id": "abeac49d"
   },
   "source": [
    "# 数据预处理\n",
    ":label:`sec_pandas`\n",
    "\n",
    "为了能用深度学习来解决现实世界的问题，我们经常从预处理原始数据开始，\n",
    "而不是从那些准备好的张量格式数据开始。\n",
    "在Python中常用的数据分析工具中，我们通常使用`pandas`软件包。\n",
    "像庞大的Python生态系统中的许多其他扩展包一样，`pandas`可以与张量兼容。\n",
    "本节我们将简要介绍使用`pandas`预处理原始数据，并将原始数据转换为张量格式的步骤。\n",
    "我们将在后面的章节中介绍更多的数据预处理技术。\n",
    "\n",
    "## 读取数据集\n",
    "\n",
    "举一个例子，我们首先(**创建一个人工数据集，并存储在CSV（逗号分隔值）文件**)\n",
    "`../data/house_tiny.csv`中。\n",
    "以其他格式存储的数据也可以通过类似的方式进行处理。\n",
    "下面我们将数据集按行写入CSV文件中。\n"
   ],
   "id": "abeac49d"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:47:43.120836Z",
     "iopub.status.busy": "2022-07-31T02:47:43.120587Z",
     "iopub.status.idle": "2022-07-31T02:47:43.130058Z",
     "shell.execute_reply": "2022-07-31T02:47:43.129398Z"
    },
    "origin_pos": 1,
    "tab": [
     "pytorch"
    ],
    "id": "3485a3af"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.makedirs(os.path.join('..', 'data'), exist_ok=True)\n",
    "data_file = os.path.join('..', 'data', 'house_tiny.csv')\n",
    "with open(data_file, 'w') as f:\n",
    "    f.write('NumRooms,Alley,Price\\n')  # 列名\n",
    "    f.write('NA,Pave,127500\\n')  # 每行表示一个数据样本\n",
    "    f.write('2,NA,106000\\n')\n",
    "    f.write('4,NA,178100\\n')\n",
    "    f.write('NA,NA,140000\\n')"
   ],
   "id": "3485a3af"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 2,
    "id": "95605a32"
   },
   "source": [
    "要[**从创建的CSV文件中加载原始数据集**]，我们导入`pandas`包并调用`read_csv`函数。该数据集有四行三列。其中每行描述了房间数量（“NumRooms”）、巷子类型（“Alley”）和房屋价格（“Price”）。\n"
   ],
   "id": "95605a32"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:47:43.133316Z",
     "iopub.status.busy": "2022-07-31T02:47:43.132861Z",
     "iopub.status.idle": "2022-07-31T02:47:43.561131Z",
     "shell.execute_reply": "2022-07-31T02:47:43.560442Z"
    },
    "origin_pos": 3,
    "tab": [
     "pytorch"
    ],
    "id": "4d067bac",
    "outputId": "e470d339-bd2d-4778-a29e-9a3a390fc0a1"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   NumRooms Alley   Price\n",
      "0       NaN  Pave  127500\n",
      "1       2.0   NaN  106000\n",
      "2       4.0   NaN  178100\n",
      "3       NaN   NaN  140000\n"
     ]
    }
   ],
   "source": [
    "# 如果没有安装pandas，只需取消对以下行的注释来安装pandas\n",
    "# !pip install pandas\n",
    "import pandas as pd\n",
    "\n",
    "data = pd.read_csv(data_file)\n",
    "print(data)"
   ],
   "id": "4d067bac"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 4,
    "id": "66b841e4"
   },
   "source": [
    "## 处理缺失值\n",
    "\n",
    "注意，“NaN”项代表缺失值。\n",
    "[**为了处理缺失的数据，典型的方法包括*插值法*和*删除法*，**]\n",
    "其中插值法用一个替代值弥补缺失值，而删除法则直接忽略缺失值。\n",
    "在(**这里，我们将考虑插值法**)。\n",
    "\n",
    "通过位置索引`iloc`，我们将`data`分成`inputs`和`outputs`，\n",
    "其中前者为`data`的前两列，而后者为`data`的最后一列。\n",
    "对于`inputs`中缺少的数值，我们用同一列的均值替换“NaN”项。\n"
   ],
   "id": "66b841e4"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:47:43.564342Z",
     "iopub.status.busy": "2022-07-31T02:47:43.563959Z",
     "iopub.status.idle": "2022-07-31T02:47:43.571872Z",
     "shell.execute_reply": "2022-07-31T02:47:43.571249Z"
    },
    "origin_pos": 5,
    "tab": [
     "pytorch"
    ],
    "id": "9feb87e7",
    "outputId": "40f53b7a-2ee9-40ae-b64d-c3aa44e6de81"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   NumRooms Alley\n",
      "0       3.0  Pave\n",
      "1       2.0   NaN\n",
      "2       4.0   NaN\n",
      "3       3.0   NaN\n"
     ]
    }
   ],
   "source": [
    "inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]\n",
    "inputs = inputs.fillna(inputs.mean())\n",
    "print(inputs)"
   ],
   "id": "9feb87e7"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 6,
    "id": "ad9920a8"
   },
   "source": [
    "[**对于`inputs`中的类别值或离散值，我们将“NaN”视为一个类别。**]\n",
    "由于“巷子类型”（“Alley”）列只接受两种类型的类别值“Pave”和“NaN”，\n",
    "`pandas`可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。\n",
    "巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1，“Alley_nan”的值设置为0。\n",
    "缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。\n"
   ],
   "id": "ad9920a8"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:47:43.574819Z",
     "iopub.status.busy": "2022-07-31T02:47:43.574477Z",
     "iopub.status.idle": "2022-07-31T02:47:43.584160Z",
     "shell.execute_reply": "2022-07-31T02:47:43.583511Z"
    },
    "origin_pos": 7,
    "tab": [
     "pytorch"
    ],
    "id": "e228a5d0",
    "outputId": "9a8048d8-5a75-401c-fbd7-a0623b535c85"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   NumRooms  Alley_Pave  Alley_nan\n",
      "0       3.0           1          0\n",
      "1       2.0           0          1\n",
      "2       4.0           0          1\n",
      "3       3.0           0          1\n"
     ]
    }
   ],
   "source": [
    "inputs = pd.get_dummies(inputs, dummy_na=True)\n",
    "print(inputs)"
   ],
   "id": "e228a5d0"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 8,
    "id": "a467868c"
   },
   "source": [
    "## 转换为张量格式\n",
    "\n",
    "[**现在`inputs`和`outputs`中的所有条目都是数值类型，它们可以转换为张量格式。**]\n",
    "当数据采用张量格式后，可以通过在 :numref:`sec_ndarray`中引入的那些张量函数来进一步操作。\n"
   ],
   "id": "a467868c"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:47:43.586980Z",
     "iopub.status.busy": "2022-07-31T02:47:43.586638Z",
     "iopub.status.idle": "2022-07-31T02:47:44.148381Z",
     "shell.execute_reply": "2022-07-31T02:47:44.147694Z"
    },
    "origin_pos": 10,
    "tab": [
     "pytorch"
    ],
    "id": "b6487a4f",
    "outputId": "9e3a0f75-12d5-4c37-b8c4-2a70f2546b14"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[3., 1., 0.],\n",
       "         [2., 0., 1.],\n",
       "         [4., 0., 1.],\n",
       "         [3., 0., 1.]], dtype=torch.float64),\n",
       " tensor([127500, 106000, 178100, 140000]))"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)\n",
    "X, y"
   ],
   "id": "b6487a4f"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 12,
    "id": "a26f9257"
   },
   "source": [
    "## 小结\n",
    "\n",
    "* `pandas`软件包是Python中常用的数据分析工具中，`pandas`可以与张量兼容。\n",
    "* 用`pandas`处理缺失的数据时，我们可根据情况选择用插值法和删除法。\n",
    "\n",
    "## 练习\n",
    "\n",
    "创建包含更多行和列的原始数据集。\n",
    "\n",
    "1. 删除缺失值最多的列。\n",
    "2. 将预处理后的数据集转换为张量格式。\n"
   ],
   "id": "a26f9257"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 0,
    "id": "2ae08dfa"
   },
   "source": [
    "# 线性代数\n",
    ":label:`sec_linear-algebra`\n",
    "\n",
    "在你已经可以存储和操作数据后，让我们简要地回顾一下部分基本线性代数内容。\n",
    "这些内容能够帮助你了解和实现本书中介绍的大多数模型。\n",
    "本节我们将介绍线性代数中的基本数学对象、算术和运算，并用数学符号和相应的代码实现来表示它们。\n",
    "\n",
    "## 标量\n",
    "\n",
    "如果你曾经在餐厅支付餐费，那么你已经知道一些基本的线性代数，比如在数字间相加或相乘。\n",
    "例如，北京的温度为$52^{\\circ}F$（除了摄氏度外，另一种温度计量单位）。\n",
    "严格来说，我们称仅包含一个数值的叫*标量*（scalar）。\n",
    "如果要将此华氏度值转换为更常用的摄氏度，\n",
    "则可以计算表达式$c=\\frac{5}{9}(f-32)$，并将$f$赋为$52$。\n",
    "在此等式中，每一项（$5$、$9$和$32$）都是标量值。\n",
    "符号$c$和$f$称为*变量*（variable），它们表示未知的标量值。\n",
    "\n",
    "在本书中，我们采用了数学表示法，其中标量变量由普通小写字母表示（例如，$x$、$y$和$z$）。\n",
    "我们用$\\mathbb{R}$表示所有（连续）*实数*标量的空间。\n",
    "我们之后将严格定义*空间*（space）是什么，\n",
    "但现在你只要记住表达式$x\\in\\mathbb{R}$是表示$x$是一个实值标量的正式形式。\n",
    "符号$\\in$称为“属于”，它表示“是集合中的成员”。\n",
    "我们可以用$x, y \\in \\{0,1\\}$来表明$x$和$y$是值只能为$0$或$1$的数字。\n",
    "\n",
    "(**标量由只有一个元素的张量表示**)。\n",
    "在下面的代码中，我们实例化两个标量，并执行一些熟悉的算术运算，即加法、乘法、除法和指数。\n"
   ],
   "id": "2ae08dfa"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:24.777213Z",
     "iopub.status.busy": "2022-07-31T02:49:24.776963Z",
     "iopub.status.idle": "2022-07-31T02:49:25.470002Z",
     "shell.execute_reply": "2022-07-31T02:49:25.469361Z"
    },
    "origin_pos": 2,
    "tab": [
     "pytorch"
    ],
    "id": "16c7367f",
    "outputId": "e54eed61-aee2-4f28-a16b-279c6725ee7a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor(5.), tensor(6.), tensor(1.5000), tensor(9.))"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "x = torch.tensor(3.0)\n",
    "y = torch.tensor(2.0)\n",
    "\n",
    "x + y, x * y, x / y, x**y"
   ],
   "id": "16c7367f"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 4,
    "id": "e8551e36"
   },
   "source": [
    "## 向量\n",
    "\n",
    "[**你可以将向量视为标量值组成的列表**]。\n",
    "我们将这些标量值称为向量的*元素*（element）或*分量*（component）。\n",
    "当向量表示数据集中的样本时，它们的值具有一定的现实意义。\n",
    "例如，如果我们正在训练一个模型来预测贷款违约风险，我们可能会将每个申请人与一个向量相关联，\n",
    "其分量与其收入、工作年限、过往违约次数和其他因素相对应。\n",
    "如果我们正在研究医院患者可能面临的心脏病发作风险，我们可能会用一个向量来表示每个患者，\n",
    "其分量为最近的生命体征、胆固醇水平、每天运动时间等。\n",
    "在数学表示法中，我们通常将向量记为粗体、小写的符号\n",
    "（例如，$\\mathbf{x}$、$\\mathbf{y}$和$\\mathbf{z})$）。\n",
    "\n",
    "我们通过一维张量处理向量。一般来说，张量可以具有任意长度，取决于机器的内存限制。\n"
   ],
   "id": "e8551e36"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.473385Z",
     "iopub.status.busy": "2022-07-31T02:49:25.473178Z",
     "iopub.status.idle": "2022-07-31T02:49:25.478396Z",
     "shell.execute_reply": "2022-07-31T02:49:25.477767Z"
    },
    "origin_pos": 6,
    "tab": [
     "pytorch"
    ],
    "id": "5d1111f6",
    "outputId": "259e1112-26fe-4a10-f4d6-67e860c28a23"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0, 1, 2, 3])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.arange(4)\n",
    "x"
   ],
   "id": "5d1111f6"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 8,
    "id": "cfe10188"
   },
   "source": [
    "我们可以使用下标来引用向量的任一元素。\n",
    "例如，我们可以通过$x_i$来引用第$i$个元素。\n",
    "注意，元素$x_i$是一个标量，所以我们在引用它时不会加粗。\n",
    "大量文献认为列向量是向量的默认方向，在本书中也是如此。\n",
    "在数学中，向量$\\mathbf{x}$可以写为：\n",
    "\n",
    "$$\\mathbf{x} =\\begin{bmatrix}x_{1}  \\\\x_{2}  \\\\ \\vdots  \\\\x_{n}\\end{bmatrix},$$\n",
    ":eqlabel:`eq_vec_def`\n",
    "\n",
    "其中$x_1,\\ldots,x_n$是向量的元素。在代码中，我们(**通过张量的索引来访问任一元素**)。\n"
   ],
   "id": "cfe10188"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.481240Z",
     "iopub.status.busy": "2022-07-31T02:49:25.481039Z",
     "iopub.status.idle": "2022-07-31T02:49:25.486169Z",
     "shell.execute_reply": "2022-07-31T02:49:25.485569Z"
    },
    "origin_pos": 10,
    "tab": [
     "pytorch"
    ],
    "id": "866866ec",
    "outputId": "03508802-a9da-4ffe-bc4e-2eaf598e9971"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(3)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[3]"
   ],
   "id": "866866ec"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 12,
    "id": "ee7c6ab6"
   },
   "source": [
    "### 长度、维度和形状\n",
    "\n",
    "向量只是一个数字数组，就像每个数组都有一个长度一样，每个向量也是如此。\n",
    "在数学表示法中，如果我们想说一个向量$\\mathbf{x}$由$n$个实值标量组成，\n",
    "我们可以将其表示为$\\mathbf{x}\\in\\mathbb{R}^n$。\n",
    "向量的长度通常称为向量的*维度*（dimension）。\n",
    "\n",
    "与普通的Python数组一样，我们可以通过调用Python的内置`len()`函数来[**访问张量的长度**]。\n"
   ],
   "id": "ee7c6ab6"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.488926Z",
     "iopub.status.busy": "2022-07-31T02:49:25.488725Z",
     "iopub.status.idle": "2022-07-31T02:49:25.493225Z",
     "shell.execute_reply": "2022-07-31T02:49:25.492640Z"
    },
    "origin_pos": 14,
    "tab": [
     "pytorch"
    ],
    "id": "5eedf6e7",
    "outputId": "71ad904e-4f89-42cd-bedf-f6c91e248e60"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(x)"
   ],
   "id": "5eedf6e7"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 16,
    "id": "7c00c251"
   },
   "source": [
    "当用张量表示一个向量（只有一个轴）时，我们也可以通过`.shape`属性访问向量的长度。\n",
    "形状（shape）是一个元素组，列出了张量沿每个轴的长度（维数）。\n",
    "对于(**只有一个轴的张量，形状只有一个元素。**)\n"
   ],
   "id": "7c00c251"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.496049Z",
     "iopub.status.busy": "2022-07-31T02:49:25.495847Z",
     "iopub.status.idle": "2022-07-31T02:49:25.500197Z",
     "shell.execute_reply": "2022-07-31T02:49:25.499609Z"
    },
    "origin_pos": 18,
    "tab": [
     "pytorch"
    ],
    "id": "c8a6a81e",
    "outputId": "d2844f4a-7002-404c-ce13-419c06a8246b"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([4])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.shape"
   ],
   "id": "c8a6a81e"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 20,
    "id": "24e8c3d5"
   },
   "source": [
    "请注意，*维度*（dimension）这个词在不同上下文时往往会有不同的含义，这经常会使人感到困惑。\n",
    "为了清楚起见，我们在此明确一下：\n",
    "*向量*或*轴*的维度被用来表示*向量*或*轴*的长度，即向量或轴的元素数量。\n",
    "然而，张量的维度用来表示张量具有的轴数。\n",
    "在这个意义上，张量的某个轴的维数就是这个轴的长度。\n",
    "\n",
    "## 矩阵\n",
    "\n",
    "正如向量将标量从零阶推广到一阶，矩阵将向量从一阶推广到二阶。\n",
    "矩阵，我们通常用粗体、大写字母来表示\n",
    "（例如，$\\mathbf{X}$、$\\mathbf{Y}$和$\\mathbf{Z}$），\n",
    "在代码中表示为具有两个轴的张量。\n",
    "\n",
    "在数学表示法中，我们使用$\\mathbf{A} \\in \\mathbb{R}^{m \\times n}$\n",
    "来表示矩阵$\\mathbf{A}$，其由$m$行和$n$列的实值标量组成。\n",
    "我们可以将任意矩阵$\\mathbf{A} \\in \\mathbb{R}^{m \\times n}$视为一个表格，\n",
    "其中每个元素$a_{ij}$属于第$i$行第$j$列：\n",
    "\n",
    "$$\\mathbf{A}=\\begin{bmatrix} a_{11} & a_{12} & \\cdots & a_{1n} \\\\ a_{21} & a_{22} & \\cdots & a_{2n} \\\\ \\vdots & \\vdots & \\ddots & \\vdots \\\\ a_{m1} & a_{m2} & \\cdots & a_{mn} \\\\ \\end{bmatrix}.$$\n",
    ":eqlabel:`eq_matrix_def`\n",
    "\n",
    "对于任意$\\mathbf{A} \\in \\mathbb{R}^{m \\times n}$，\n",
    "$\\mathbf{A}$的形状是（$m$,$n$）或$m \\times n$。\n",
    "当矩阵具有相同数量的行和列时，其形状将变为正方形；\n",
    "因此，它被称为*方阵*（square matrix）。\n",
    "\n",
    "当调用函数来实例化张量时，\n",
    "我们可以[**通过指定两个分量$m$和$n$来创建一个形状为$m \\times n$的矩阵**]。\n"
   ],
   "id": "24e8c3d5"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.503025Z",
     "iopub.status.busy": "2022-07-31T02:49:25.502706Z",
     "iopub.status.idle": "2022-07-31T02:49:25.507724Z",
     "shell.execute_reply": "2022-07-31T02:49:25.507121Z"
    },
    "origin_pos": 22,
    "tab": [
     "pytorch"
    ],
    "id": "ee2eeee4",
    "outputId": "e776165f-7155-4efd-f9a9-50a359583f99"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7],\n",
       "        [ 8,  9, 10, 11],\n",
       "        [12, 13, 14, 15],\n",
       "        [16, 17, 18, 19]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = torch.arange(20).reshape(5, 4)\n",
    "A"
   ],
   "id": "ee2eeee4"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 24,
    "id": "f99494b0"
   },
   "source": [
    "我们可以通过行索引（$i$）和列索引（$j$）来访问矩阵中的标量元素$a_{ij}$，\n",
    "例如$[\\mathbf{A}]_{ij}$。\n",
    "如果没有给出矩阵$\\mathbf{A}$的标量元素，如在 :eqref:`eq_matrix_def`那样，\n",
    "我们可以简单地使用矩阵$\\mathbf{A}$的小写字母索引下标$a_{ij}$\n",
    "来引用$[\\mathbf{A}]_{ij}$。\n",
    "为了表示起来简单，只有在必要时才会将逗号插入到单独的索引中，\n",
    "例如$a_{2,3j}$和$[\\mathbf{A}]_{2i-1,3}$。\n",
    "\n",
    "当我们交换矩阵的行和列时，结果称为矩阵的*转置*（transpose）。\n",
    "我们用$\\mathbf{a}^\\top$来表示矩阵的转置，如果$\\mathbf{B}=\\mathbf{A}^\\top$，\n",
    "则对于任意$i$和$j$，都有$b_{ij}=a_{ji}$。\n",
    "因此，在 :eqref:`eq_matrix_def`中的转置是一个形状为$n \\times m$的矩阵：\n",
    "\n",
    "$$\n",
    "\\mathbf{A}^\\top =\n",
    "\\begin{bmatrix}\n",
    "    a_{11} & a_{21} & \\dots  & a_{m1} \\\\\n",
    "    a_{12} & a_{22} & \\dots  & a_{m2} \\\\\n",
    "    \\vdots & \\vdots & \\ddots  & \\vdots \\\\\n",
    "    a_{1n} & a_{2n} & \\dots  & a_{mn}\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "现在我们在代码中访问(**矩阵的转置**)。\n"
   ],
   "id": "f99494b0"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.511328Z",
     "iopub.status.busy": "2022-07-31T02:49:25.510560Z",
     "iopub.status.idle": "2022-07-31T02:49:25.517098Z",
     "shell.execute_reply": "2022-07-31T02:49:25.516299Z"
    },
    "origin_pos": 26,
    "tab": [
     "pytorch"
    ],
    "id": "a8233d91",
    "outputId": "6cf9a9af-4d17-4c2d-cec5-c448c7c60f85"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0,  4,  8, 12, 16],\n",
       "        [ 1,  5,  9, 13, 17],\n",
       "        [ 2,  6, 10, 14, 18],\n",
       "        [ 3,  7, 11, 15, 19]])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.T"
   ],
   "id": "a8233d91"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 28,
    "id": "237df38a"
   },
   "source": [
    "作为方阵的一种特殊类型，[***对称矩阵*（symmetric matrix）$\\mathbf{A}$等于其转置：$\\mathbf{A} = \\mathbf{A}^\\top$**]。\n",
    "这里我们定义一个对称矩阵$\\mathbf{B}$：\n"
   ],
   "id": "237df38a"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.521175Z",
     "iopub.status.busy": "2022-07-31T02:49:25.520512Z",
     "iopub.status.idle": "2022-07-31T02:49:25.527941Z",
     "shell.execute_reply": "2022-07-31T02:49:25.527038Z"
    },
    "origin_pos": 30,
    "tab": [
     "pytorch"
    ],
    "id": "0cf6084f",
    "outputId": "86100600-06f1-4865-eaad-ee6056755a90"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1, 2, 3],\n",
       "        [2, 0, 4],\n",
       "        [3, 4, 5]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])\n",
    "B"
   ],
   "id": "0cf6084f"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 32,
    "id": "fa3e7711"
   },
   "source": [
    "现在我们将`B`与它的转置进行比较。\n"
   ],
   "id": "fa3e7711"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.531482Z",
     "iopub.status.busy": "2022-07-31T02:49:25.530835Z",
     "iopub.status.idle": "2022-07-31T02:49:25.537377Z",
     "shell.execute_reply": "2022-07-31T02:49:25.536523Z"
    },
    "origin_pos": 34,
    "tab": [
     "pytorch"
    ],
    "id": "821553ca",
    "outputId": "a50ded13-3f76-45e5-a151-900ea1ffb50c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[True, True, True],\n",
       "        [True, True, True],\n",
       "        [True, True, True]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B == B.T"
   ],
   "id": "821553ca"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 36,
    "id": "fcf519e3"
   },
   "source": [
    "矩阵是有用的数据结构：它们允许我们组织具有不同模式的数据。\n",
    "例如，我们矩阵中的行可能对应于不同的房屋（数据样本），而列可能对应于不同的属性。\n",
    "如果你曾经使用过电子表格软件或已阅读过 :numref:`sec_pandas`，这应该听起来很熟悉。\n",
    "因此，尽管单个向量的默认方向是列向量，但在表示表格数据集的矩阵中，\n",
    "将每个数据样本作为矩阵中的行向量更为常见。\n",
    "我们将在后面的章节中讲到这点，这种约定将支持常见的深度学习实践。\n",
    "例如，沿着张量的最外轴，我们可以访问或遍历小批量的数据样本。\n",
    "\n",
    "\n",
    "## 张量\n",
    "\n",
    "[**就像向量是标量的推广，矩阵是向量的推广一样，我们可以构建具有更多轴的数据结构**]。\n",
    "张量（本小节中的“张量”指代数对象）为我们提供了描述具有任意数量轴的$n$维数组的通用方法。\n",
    "例如，向量是一阶张量，矩阵是二阶张量。\n",
    "张量用特殊字体的大写字母表示（例如，$\\mathsf{X}$、$\\mathsf{Y}$和$\\mathsf{Z}$），\n",
    "它们的索引机制（例如$x_{ijk}$和$[\\mathsf{X}]_{1,2i-1,3}$）与矩阵类似。\n",
    "\n",
    "当我们开始处理图像时，张量将变得更加重要，图像以$n$维数组形式出现，\n",
    "其中3个轴对应于高度、宽度，以及一个*通道*（channel）轴，\n",
    "用于表示颜色通道（红色、绿色和蓝色）。\n",
    "现在，我们先将高阶张量暂放一边，而是专注学习其基础知识。\n"
   ],
   "id": "fcf519e3"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.541361Z",
     "iopub.status.busy": "2022-07-31T02:49:25.540795Z",
     "iopub.status.idle": "2022-07-31T02:49:25.547941Z",
     "shell.execute_reply": "2022-07-31T02:49:25.547140Z"
    },
    "origin_pos": 38,
    "tab": [
     "pytorch"
    ],
    "id": "169d01b2",
    "outputId": "e3caead1-468b-4259-b9de-89cb391eba8b"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[ 0,  1,  2,  3],\n",
       "         [ 4,  5,  6,  7],\n",
       "         [ 8,  9, 10, 11]],\n",
       "\n",
       "        [[12, 13, 14, 15],\n",
       "         [16, 17, 18, 19],\n",
       "         [20, 21, 22, 23]]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = torch.arange(24).reshape(2, 3, 4)\n",
    "X"
   ],
   "id": "169d01b2"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 40,
    "id": "57454bea"
   },
   "source": [
    "## 张量算法的基本性质\n",
    "\n",
    "标量、向量、矩阵和任意数量轴的张量（本小节中的“张量”指代数对象）有一些实用的属性。\n",
    "例如，你可能已经从按元素操作的定义中注意到，任何按元素的一元运算都不会改变其操作数的形状。\n",
    "同样，[**给定具有相同形状的任意两个张量，任何按元素二元运算的结果都将是相同形状的张量**]。\n",
    "例如，将两个相同形状的矩阵相加，会在这两个矩阵上执行元素加法。\n"
   ],
   "id": "57454bea"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.551992Z",
     "iopub.status.busy": "2022-07-31T02:49:25.551379Z",
     "iopub.status.idle": "2022-07-31T02:49:25.560709Z",
     "shell.execute_reply": "2022-07-31T02:49:25.559920Z"
    },
    "origin_pos": 42,
    "tab": [
     "pytorch"
    ],
    "id": "e2fd5df8",
    "outputId": "3e620015-6e46-4423-c105-c6bec02d87c2"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[ 0.,  1.,  2.,  3.],\n",
       "         [ 4.,  5.,  6.,  7.],\n",
       "         [ 8.,  9., 10., 11.],\n",
       "         [12., 13., 14., 15.],\n",
       "         [16., 17., 18., 19.]]),\n",
       " tensor([[ 0.,  2.,  4.,  6.],\n",
       "         [ 8., 10., 12., 14.],\n",
       "         [16., 18., 20., 22.],\n",
       "         [24., 26., 28., 30.],\n",
       "         [32., 34., 36., 38.]]))"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = torch.arange(20, dtype=torch.float32).reshape(5, 4)\n",
    "B = A.clone()  # 通过分配新内存，将A的一个副本分配给B\n",
    "A, A + B"
   ],
   "id": "e2fd5df8"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 44,
    "id": "22fe9b63"
   },
   "source": [
    "具体而言，[**两个矩阵的按元素乘法称为*Hadamard积*（Hadamard product）（数学符号$\\odot$）**]。\n",
    "对于矩阵$\\mathbf{B} \\in \\mathbb{R}^{m \\times n}$，\n",
    "其中第$i$行和第$j$列的元素是$b_{ij}$。\n",
    "矩阵$\\mathbf{A}$（在 :eqref:`eq_matrix_def`中定义）和$\\mathbf{B}$的Hadamard积为：\n",
    "\n",
    "$$\n",
    "\\mathbf{A} \\odot \\mathbf{B} =\n",
    "\\begin{bmatrix}\n",
    "    a_{11}  b_{11} & a_{12}  b_{12} & \\dots  & a_{1n}  b_{1n} \\\\\n",
    "    a_{21}  b_{21} & a_{22}  b_{22} & \\dots  & a_{2n}  b_{2n} \\\\\n",
    "    \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    "    a_{m1}  b_{m1} & a_{m2}  b_{m2} & \\dots  & a_{mn}  b_{mn}\n",
    "\\end{bmatrix}.\n",
    "$$\n"
   ],
   "id": "22fe9b63"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.564286Z",
     "iopub.status.busy": "2022-07-31T02:49:25.563751Z",
     "iopub.status.idle": "2022-07-31T02:49:25.568979Z",
     "shell.execute_reply": "2022-07-31T02:49:25.568278Z"
    },
    "origin_pos": 46,
    "tab": [
     "pytorch"
    ],
    "id": "f399c8f7",
    "outputId": "990af1b0-eb6b-44f7-bf81-f3a61cf17709"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[  0.,   1.,   4.,   9.],\n",
       "        [ 16.,  25.,  36.,  49.],\n",
       "        [ 64.,  81., 100., 121.],\n",
       "        [144., 169., 196., 225.],\n",
       "        [256., 289., 324., 361.]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * B"
   ],
   "id": "f399c8f7"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 48,
    "id": "1d92cbbd"
   },
   "source": [
    "将张量乘以或加上一个标量不会改变张量的形状，其中张量的每个元素都将与标量相加或相乘。\n"
   ],
   "id": "1d92cbbd"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.572916Z",
     "iopub.status.busy": "2022-07-31T02:49:25.572220Z",
     "iopub.status.idle": "2022-07-31T02:49:25.579768Z",
     "shell.execute_reply": "2022-07-31T02:49:25.578881Z"
    },
    "origin_pos": 50,
    "tab": [
     "pytorch"
    ],
    "id": "b02cec19",
    "outputId": "6388b752-617a-495a-9493-5ef86487db6a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[[ 2,  3,  4,  5],\n",
       "          [ 6,  7,  8,  9],\n",
       "          [10, 11, 12, 13]],\n",
       " \n",
       "         [[14, 15, 16, 17],\n",
       "          [18, 19, 20, 21],\n",
       "          [22, 23, 24, 25]]]),\n",
       " torch.Size([2, 3, 4]))"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 2\n",
    "X = torch.arange(24).reshape(2, 3, 4)\n",
    "a + X, (a * X).shape"
   ],
   "id": "b02cec19"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 52,
    "id": "7e403d8a"
   },
   "source": [
    "## 降维\n",
    ":label:`subseq_lin-alg-reduction`\n",
    "\n",
    "我们可以对任意张量进行的一个有用的操作是[**计算其元素的和**]。\n",
    "在数学表示法中，我们使用$\\sum$符号表示求和。\n",
    "为了表示长度为$d$的向量中元素的总和，可以记为$\\sum_{i=1}^dx_i$。\n",
    "在代码中，我们可以调用计算求和的函数：\n"
   ],
   "id": "7e403d8a"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.584063Z",
     "iopub.status.busy": "2022-07-31T02:49:25.583278Z",
     "iopub.status.idle": "2022-07-31T02:49:25.590872Z",
     "shell.execute_reply": "2022-07-31T02:49:25.590071Z"
    },
    "origin_pos": 54,
    "tab": [
     "pytorch"
    ],
    "id": "638088a6",
    "outputId": "ffb201b3-20da-4d5e-ea08-0fd2ea9d1b7f"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([0., 1., 2., 3.]), tensor(6.))"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.arange(4, dtype=torch.float32)\n",
    "x, x.sum()"
   ],
   "id": "638088a6"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 56,
    "id": "af4d2990"
   },
   "source": [
    "我们可以(**表示任意形状张量的元素和**)。\n",
    "例如，矩阵$\\mathbf{A}$中元素的和可以记为$\\sum_{i=1}^{m} \\sum_{j=1}^{n} a_{ij}$。\n"
   ],
   "id": "af4d2990"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.594497Z",
     "iopub.status.busy": "2022-07-31T02:49:25.593825Z",
     "iopub.status.idle": "2022-07-31T02:49:25.600592Z",
     "shell.execute_reply": "2022-07-31T02:49:25.599705Z"
    },
    "origin_pos": 58,
    "tab": [
     "pytorch"
    ],
    "id": "6defc2f9",
    "outputId": "635a13e8-7be0-4fd0-c6ab-2bdb21c8aa09"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(torch.Size([5, 4]), tensor(190.))"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.shape, A.sum()"
   ],
   "id": "6defc2f9"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 60,
    "id": "43e6d8bb"
   },
   "source": [
    "默认情况下，调用求和函数会沿所有的轴降低张量的维度，使它变为一个标量。\n",
    "我们还可以[**指定张量沿哪一个轴来通过求和降低维度**]。\n",
    "以矩阵为例，为了通过求和所有行的元素来降维（轴0），我们可以在调用函数时指定`axis=0`。\n",
    "由于输入矩阵沿0轴降维以生成输出向量，因此输入轴0的维数在输出形状中消失。\n"
   ],
   "id": "43e6d8bb"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.604592Z",
     "iopub.status.busy": "2022-07-31T02:49:25.603910Z",
     "iopub.status.idle": "2022-07-31T02:49:25.611111Z",
     "shell.execute_reply": "2022-07-31T02:49:25.610252Z"
    },
    "origin_pos": 62,
    "tab": [
     "pytorch"
    ],
    "id": "e7fc6828",
    "outputId": "7328ce9b-14e2-49a5-dca3-7a73c1764bab"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([40., 45., 50., 55.]), torch.Size([4]))"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_sum_axis0 = A.sum(axis=0)\n",
    "A_sum_axis0, A_sum_axis0.shape"
   ],
   "id": "e7fc6828"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 64,
    "id": "f1725406"
   },
   "source": [
    "指定`axis=1`将通过汇总所有列的元素降维（轴1）。因此，输入轴1的维数在输出形状中消失。\n"
   ],
   "id": "f1725406"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.614669Z",
     "iopub.status.busy": "2022-07-31T02:49:25.614007Z",
     "iopub.status.idle": "2022-07-31T02:49:25.620151Z",
     "shell.execute_reply": "2022-07-31T02:49:25.619453Z"
    },
    "origin_pos": 66,
    "tab": [
     "pytorch"
    ],
    "id": "95eeefed",
    "outputId": "85f5e8a1-b034-4aff-eea6-ee58e5447fe9"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_sum_axis1 = A.sum(axis=1)\n",
    "A_sum_axis1, A_sum_axis1.shape"
   ],
   "id": "95eeefed"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 68,
    "id": "9ce6ba70"
   },
   "source": [
    "沿着行和列对矩阵求和，等价于对矩阵的所有元素进行求和。\n"
   ],
   "id": "9ce6ba70"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.623974Z",
     "iopub.status.busy": "2022-07-31T02:49:25.623410Z",
     "iopub.status.idle": "2022-07-31T02:49:25.629292Z",
     "shell.execute_reply": "2022-07-31T02:49:25.628640Z"
    },
    "origin_pos": 70,
    "tab": [
     "pytorch"
    ],
    "id": "a898e0b9",
    "outputId": "d5de3c66-66da-4283-9c95-2b6b50b09e24"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(190.)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.sum(axis=[0, 1])  # SameasA.sum()"
   ],
   "id": "a898e0b9"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 72,
    "id": "310d7d17"
   },
   "source": [
    "[**一个与求和相关的量是*平均值*（mean或average）**]。\n",
    "我们通过将总和除以元素总数来计算平均值。\n",
    "在代码中，我们可以调用函数来计算任意形状张量的平均值。\n"
   ],
   "id": "310d7d17"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.632603Z",
     "iopub.status.busy": "2022-07-31T02:49:25.632241Z",
     "iopub.status.idle": "2022-07-31T02:49:25.638012Z",
     "shell.execute_reply": "2022-07-31T02:49:25.637385Z"
    },
    "origin_pos": 74,
    "tab": [
     "pytorch"
    ],
    "id": "f09e21c3",
    "outputId": "1ece00b0-844d-4f3e-b042-c321d97dd54c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor(9.5000), tensor(9.5000))"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.mean(), A.sum() / A.numel()"
   ],
   "id": "f09e21c3"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 76,
    "id": "4d3a172b"
   },
   "source": [
    "同样，计算平均值的函数也可以沿指定轴降低张量的维度。\n"
   ],
   "id": "4d3a172b"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.640796Z",
     "iopub.status.busy": "2022-07-31T02:49:25.640448Z",
     "iopub.status.idle": "2022-07-31T02:49:25.646369Z",
     "shell.execute_reply": "2022-07-31T02:49:25.645717Z"
    },
    "origin_pos": 78,
    "tab": [
     "pytorch"
    ],
    "id": "0212e96d",
    "outputId": "c3e63ef2-357d-498d-cb71-09faaaa147ce"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.]))"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.mean(axis=0), A.sum(axis=0) / A.shape[0]"
   ],
   "id": "0212e96d"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 80,
    "id": "69e68eef"
   },
   "source": [
    "### 非降维求和\n",
    ":label:`subseq_lin-alg-non-reduction`\n",
    "\n",
    "但是，有时在调用函数来[**计算总和或均值时保持轴数不变**]会很有用。\n"
   ],
   "id": "69e68eef"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.649666Z",
     "iopub.status.busy": "2022-07-31T02:49:25.649466Z",
     "iopub.status.idle": "2022-07-31T02:49:25.655157Z",
     "shell.execute_reply": "2022-07-31T02:49:25.654470Z"
    },
    "origin_pos": 82,
    "tab": [
     "pytorch"
    ],
    "id": "c35299aa",
    "outputId": "25df2ddf-9f0a-44fe-ddac-e87c38088493"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 6.],\n",
       "        [22.],\n",
       "        [38.],\n",
       "        [54.],\n",
       "        [70.]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum_A = A.sum(axis=1, keepdims=True)\n",
    "sum_A"
   ],
   "id": "c35299aa"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 84,
    "id": "25889769"
   },
   "source": [
    "例如，由于`sum_A`在对每行进行求和后仍保持两个轴，我们可以(**通过广播将`A`除以`sum_A`**)。\n"
   ],
   "id": "25889769"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.658240Z",
     "iopub.status.busy": "2022-07-31T02:49:25.657743Z",
     "iopub.status.idle": "2022-07-31T02:49:25.663240Z",
     "shell.execute_reply": "2022-07-31T02:49:25.662591Z"
    },
    "origin_pos": 86,
    "tab": [
     "pytorch"
    ],
    "id": "7b5fe10f",
    "outputId": "3f3dd393-c6ed-4449-eaa6-79c839944dcd"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.0000, 0.1667, 0.3333, 0.5000],\n",
       "        [0.1818, 0.2273, 0.2727, 0.3182],\n",
       "        [0.2105, 0.2368, 0.2632, 0.2895],\n",
       "        [0.2222, 0.2407, 0.2593, 0.2778],\n",
       "        [0.2286, 0.2429, 0.2571, 0.2714]])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A / sum_A"
   ],
   "id": "7b5fe10f"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 88,
    "id": "9f145e9e"
   },
   "source": [
    "如果我们想沿[**某个轴计算`A`元素的累积总和**]，\n",
    "比如`axis=0`（按行计算），我们可以调用`cumsum`函数。\n",
    "此函数不会沿任何轴降低输入张量的维度。\n"
   ],
   "id": "9f145e9e"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.666763Z",
     "iopub.status.busy": "2022-07-31T02:49:25.666257Z",
     "iopub.status.idle": "2022-07-31T02:49:25.672211Z",
     "shell.execute_reply": "2022-07-31T02:49:25.671493Z"
    },
    "origin_pos": 90,
    "tab": [
     "pytorch"
    ],
    "id": "06cc445d",
    "outputId": "5c185383-bdb5-4394-c5a6-7d7f017a08cc"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0.,  1.,  2.,  3.],\n",
       "        [ 4.,  6.,  8., 10.],\n",
       "        [12., 15., 18., 21.],\n",
       "        [24., 28., 32., 36.],\n",
       "        [40., 45., 50., 55.]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.cumsum(axis=0)"
   ],
   "id": "06cc445d"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 92,
    "id": "ece77519"
   },
   "source": [
    "## 点积（Dot Product）\n",
    "\n",
    "我们已经学习了按元素操作、求和及平均值。\n",
    "另一个最基本的操作之一是点积。\n",
    "给定两个向量$\\mathbf{x},\\mathbf{y}\\in\\mathbb{R}^d$，\n",
    "它们的*点积*（dot product）$\\mathbf{x}^\\top\\mathbf{y}$\n",
    "（或$\\langle\\mathbf{x},\\mathbf{y}\\rangle$）\n",
    "是相同位置的按元素乘积的和：$\\mathbf{x}^\\top \\mathbf{y} = \\sum_{i=1}^{d} x_i y_i$。\n",
    "\n",
    "[~~点积是相同位置的按元素乘积的和~~]\n"
   ],
   "id": "ece77519"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.675225Z",
     "iopub.status.busy": "2022-07-31T02:49:25.674701Z",
     "iopub.status.idle": "2022-07-31T02:49:25.681409Z",
     "shell.execute_reply": "2022-07-31T02:49:25.680678Z"
    },
    "origin_pos": 94,
    "tab": [
     "pytorch"
    ],
    "id": "9bc65041",
    "outputId": "ac01d02d-1380-4ac2-ad31-3136f2dbe45f"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = torch.ones(4, dtype = torch.float32)\n",
    "x, y, torch.dot(x, y)"
   ],
   "id": "9bc65041"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 96,
    "id": "f3b94fab"
   },
   "source": [
    "注意，(**我们可以通过执行按元素乘法，然后进行求和来表示两个向量的点积**)：\n"
   ],
   "id": "f3b94fab"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.685162Z",
     "iopub.status.busy": "2022-07-31T02:49:25.684503Z",
     "iopub.status.idle": "2022-07-31T02:49:25.690185Z",
     "shell.execute_reply": "2022-07-31T02:49:25.689361Z"
    },
    "origin_pos": 98,
    "tab": [
     "pytorch"
    ],
    "id": "b85defe2",
    "outputId": "00f3a6e2-8e50-445f-ec7c-7a15058efba5"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(6.)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.sum(x * y)"
   ],
   "id": "b85defe2"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 100,
    "id": "67a8520d"
   },
   "source": [
    "点积在很多场合都很有用。\n",
    "例如，给定一组由向量$\\mathbf{x} \\in \\mathbb{R}^d$表示的值，\n",
    "和一组由$\\mathbf{w} \\in \\mathbb{R}^d$表示的权重。\n",
    "$\\mathbf{x}$中的值根据权重$\\mathbf{w}$的加权和，\n",
    "可以表示为点积$\\mathbf{x}^\\top \\mathbf{w}$。\n",
    "当权重为非负数且和为1（即$\\left(\\sum_{i=1}^{d}{w_i}=1\\right)$）时，\n",
    "点积表示*加权平均*（weighted average）。\n",
    "将两个向量规范化得到单位长度后，点积表示它们夹角的余弦。\n",
    "我们将在本节的后面正式介绍*长度*（length）的概念。\n",
    "\n",
    "## 矩阵-向量积\n",
    "\n",
    "现在我们知道如何计算点积，我们可以开始理解*矩阵-向量积*（matrix-vector product）。\n",
    "回顾分别在 :eqref:`eq_matrix_def`和 :eqref:`eq_vec_def`中定义的矩阵$\\mathbf{A} \\in \\mathbb{R}^{m \\times n}$和向量$\\mathbf{x} \\in \\mathbb{R}^n$。\n",
    "让我们将矩阵$\\mathbf{A}$用它的行向量表示：\n",
    "\n",
    "$$\\mathbf{A}=\n",
    "\\begin{bmatrix}\n",
    "\\mathbf{a}^\\top_{1} \\\\\n",
    "\\mathbf{a}^\\top_{2} \\\\\n",
    "\\vdots \\\\\n",
    "\\mathbf{a}^\\top_m \\\\\n",
    "\\end{bmatrix},$$\n",
    "\n",
    "其中每个$\\mathbf{a}^\\top_{i} \\in \\mathbb{R}^n$都是行向量，表示矩阵的第$i$行。\n",
    "[**矩阵向量积$\\mathbf{A}\\mathbf{x}$是一个长度为$m$的列向量，\n",
    "其第$i$个元素是点积$\\mathbf{a}^\\top_i \\mathbf{x}$**]：\n",
    "\n",
    "$$\n",
    "\\mathbf{A}\\mathbf{x}\n",
    "= \\begin{bmatrix}\n",
    "\\mathbf{a}^\\top_{1} \\\\\n",
    "\\mathbf{a}^\\top_{2} \\\\\n",
    "\\vdots \\\\\n",
    "\\mathbf{a}^\\top_m \\\\\n",
    "\\end{bmatrix}\\mathbf{x}\n",
    "= \\begin{bmatrix}\n",
    " \\mathbf{a}^\\top_{1} \\mathbf{x}  \\\\\n",
    " \\mathbf{a}^\\top_{2} \\mathbf{x} \\\\\n",
    "\\vdots\\\\\n",
    " \\mathbf{a}^\\top_{m} \\mathbf{x}\\\\\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "我们可以把一个矩阵$\\mathbf{A} \\in \\mathbb{R}^{m \\times n}$乘法看作是一个从$\\mathbb{R}^{n}$到$\\mathbb{R}^{m}$向量的转换。\n",
    "这些转换是非常有用的。例如，我们可以用方阵的乘法来表示旋转。\n",
    "我们将在后续章节中讲到，我们也可以使用矩阵-向量积来描述在给定前一层的值时，\n",
    "求解神经网络每一层所需的复杂计算。\n"
   ],
   "id": "67a8520d"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 102,
    "tab": [
     "pytorch"
    ],
    "id": "a537ea48"
   },
   "source": [
    "在代码中使用张量表示矩阵-向量积，我们使用与点积相同的`mv`函数。\n",
    "当我们为矩阵`A`和向量`x`调用`torch.mv(A, x)`时，会执行矩阵-向量积。\n",
    "注意，`A`的列维数（沿轴1的长度）必须与`x`的维数（其长度）相同。\n"
   ],
   "id": "a537ea48"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.693867Z",
     "iopub.status.busy": "2022-07-31T02:49:25.693189Z",
     "iopub.status.idle": "2022-07-31T02:49:25.698677Z",
     "shell.execute_reply": "2022-07-31T02:49:25.697979Z"
    },
    "origin_pos": 105,
    "tab": [
     "pytorch"
    ],
    "id": "d498f599",
    "outputId": "cde5e13e-4ffe-404b-a030-ae23f43c4fde"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.shape, x.shape, torch.mv(A, x)"
   ],
   "id": "d498f599"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 107,
    "id": "c2d6cbe8"
   },
   "source": [
    "## 矩阵-矩阵乘法\n",
    "\n",
    "如果你已经掌握了点积和矩阵-向量积的知识，\n",
    "那么**矩阵-矩阵乘法**（matrix-matrix multiplication）应该很简单。\n",
    "\n",
    "假设我们有两个矩阵$\\mathbf{A} \\in \\mathbb{R}^{n \\times k}$和$\\mathbf{B} \\in \\mathbb{R}^{k \\times m}$：\n",
    "\n",
    "$$\\mathbf{A}=\\begin{bmatrix}\n",
    " a_{11} & a_{12} & \\cdots & a_{1k} \\\\\n",
    " a_{21} & a_{22} & \\cdots & a_{2k} \\\\\n",
    "\\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    " a_{n1} & a_{n2} & \\cdots & a_{nk} \\\\\n",
    "\\end{bmatrix},\\quad\n",
    "\\mathbf{B}=\\begin{bmatrix}\n",
    " b_{11} & b_{12} & \\cdots & b_{1m} \\\\\n",
    " b_{21} & b_{22} & \\cdots & b_{2m} \\\\\n",
    "\\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    " b_{k1} & b_{k2} & \\cdots & b_{km} \\\\\n",
    "\\end{bmatrix}.$$\n",
    "\n",
    "用行向量$\\mathbf{a}^\\top_{i} \\in \\mathbb{R}^k$表示矩阵$\\mathbf{A}$的第$i$行，并让列向量$\\mathbf{b}_{j} \\in \\mathbb{R}^k$作为矩阵$\\mathbf{B}$的第$j$列。要生成矩阵积$\\mathbf{C} = \\mathbf{A}\\mathbf{B}$，最简单的方法是考虑$\\mathbf{A}$的行向量和$\\mathbf{B}$的列向量:\n",
    "\n",
    "$$\\mathbf{A}=\n",
    "\\begin{bmatrix}\n",
    "\\mathbf{a}^\\top_{1} \\\\\n",
    "\\mathbf{a}^\\top_{2} \\\\\n",
    "\\vdots \\\\\n",
    "\\mathbf{a}^\\top_n \\\\\n",
    "\\end{bmatrix},\n",
    "\\quad \\mathbf{B}=\\begin{bmatrix}\n",
    " \\mathbf{b}_{1} & \\mathbf{b}_{2} & \\cdots & \\mathbf{b}_{m} \\\\\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "当我们简单地将每个元素$c_{ij}$计算为点积$\\mathbf{a}^\\top_i \\mathbf{b}_j$:\n",
    "\n",
    "$$\\mathbf{C} = \\mathbf{AB} = \\begin{bmatrix}\n",
    "\\mathbf{a}^\\top_{1} \\\\\n",
    "\\mathbf{a}^\\top_{2} \\\\\n",
    "\\vdots \\\\\n",
    "\\mathbf{a}^\\top_n \\\\\n",
    "\\end{bmatrix}\n",
    "\\begin{bmatrix}\n",
    " \\mathbf{b}_{1} & \\mathbf{b}_{2} & \\cdots & \\mathbf{b}_{m} \\\\\n",
    "\\end{bmatrix}\n",
    "= \\begin{bmatrix}\n",
    "\\mathbf{a}^\\top_{1} \\mathbf{b}_1 & \\mathbf{a}^\\top_{1}\\mathbf{b}_2& \\cdots & \\mathbf{a}^\\top_{1} \\mathbf{b}_m \\\\\n",
    " \\mathbf{a}^\\top_{2}\\mathbf{b}_1 & \\mathbf{a}^\\top_{2} \\mathbf{b}_2 & \\cdots & \\mathbf{a}^\\top_{2} \\mathbf{b}_m \\\\\n",
    " \\vdots & \\vdots & \\ddots &\\vdots\\\\\n",
    "\\mathbf{a}^\\top_{n} \\mathbf{b}_1 & \\mathbf{a}^\\top_{n}\\mathbf{b}_2& \\cdots& \\mathbf{a}^\\top_{n} \\mathbf{b}_m\n",
    "\\end{bmatrix}.\n",
    "$$\n",
    "\n",
    "[**我们可以将矩阵-矩阵乘法$\\mathbf{AB}$看作是简单地执行$m$次矩阵-向量积，并将结果拼接在一起，形成一个$n \\times m$矩阵**]。\n",
    "在下面的代码中，我们在`A`和`B`上执行矩阵乘法。\n",
    "这里的`A`是一个5行4列的矩阵，`B`是一个4行3列的矩阵。\n",
    "两者相乘后，我们得到了一个5行3列的矩阵。\n"
   ],
   "id": "c2d6cbe8"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.702116Z",
     "iopub.status.busy": "2022-07-31T02:49:25.701512Z",
     "iopub.status.idle": "2022-07-31T02:49:25.707439Z",
     "shell.execute_reply": "2022-07-31T02:49:25.706770Z"
    },
    "origin_pos": 109,
    "tab": [
     "pytorch"
    ],
    "id": "de51b827",
    "outputId": "9356a933-b542-43e0-e056-3bcb95e55758"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 6.,  6.,  6.],\n",
       "        [22., 22., 22.],\n",
       "        [38., 38., 38.],\n",
       "        [54., 54., 54.],\n",
       "        [70., 70., 70.]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = torch.ones(4, 3)\n",
    "torch.mm(A, B)"
   ],
   "id": "de51b827"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 111,
    "id": "89e091f3"
   },
   "source": [
    "矩阵-矩阵乘法可以简单地称为**矩阵乘法**，不应与\"Hadamard积\"混淆。\n",
    "\n",
    "## 范数\n",
    ":label:`subsec_lin-algebra-norms`\n",
    "\n",
    "线性代数中最有用的一些运算符是*范数*（norm）。\n",
    "非正式地说，一个向量的*范数*告诉我们一个向量有多大。\n",
    "这里考虑的*大小*（size）概念不涉及维度，而是分量的大小。\n",
    "\n",
    "在线性代数中，向量范数是将向量映射到标量的函数$f$。\n",
    "给定任意向量$\\mathbf{x}$，向量范数要满足一些属性。\n",
    "第一个性质是：如果我们按常数因子$\\alpha$缩放向量的所有元素，\n",
    "其范数也会按相同常数因子的*绝对值*缩放：\n",
    "\n",
    "$$f(\\alpha \\mathbf{x}) = |\\alpha| f(\\mathbf{x}).$$\n",
    "\n",
    "第二个性质是我们熟悉的三角不等式:\n",
    "\n",
    "$$f(\\mathbf{x} + \\mathbf{y}) \\leq f(\\mathbf{x}) + f(\\mathbf{y}).$$\n",
    "\n",
    "第三个性质简单地说范数必须是非负的:\n",
    "\n",
    "$$f(\\mathbf{x}) \\geq 0.$$\n",
    "\n",
    "这是有道理的。因为在大多数情况下，任何东西的最小的*大小*是0。\n",
    "最后一个性质要求范数最小为0，当且仅当向量全由0组成。\n",
    "\n",
    "$$\\forall i, [\\mathbf{x}]_i = 0 \\Leftrightarrow f(\\mathbf{x})=0.$$\n",
    "\n",
    "你可能会注意到，范数听起来很像距离的度量。\n",
    "如果你还记得欧几里得距离和毕达哥拉斯定理，那么非负性的概念和三角不等式可能会给你一些启发。\n",
    "事实上，欧几里得距离是一个$L_2$范数：\n",
    "假设$n$维向量$\\mathbf{x}$中的元素是$x_1,\\ldots,x_n$，其[**$L_2$*范数*是向量元素平方和的平方根：**]\n",
    "\n",
    "(**$$\\|\\mathbf{x}\\|_2 = \\sqrt{\\sum_{i=1}^n x_i^2},$$**)\n",
    "\n",
    "其中，在$L_2$范数中常常省略下标$2$，也就是说$\\|\\mathbf{x}\\|$等同于$\\|\\mathbf{x}\\|_2$。\n",
    "在代码中，我们可以按如下方式计算向量的$L_2$范数。\n"
   ],
   "id": "89e091f3"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.710886Z",
     "iopub.status.busy": "2022-07-31T02:49:25.710388Z",
     "iopub.status.idle": "2022-07-31T02:49:25.715538Z",
     "shell.execute_reply": "2022-07-31T02:49:25.714892Z"
    },
    "origin_pos": 113,
    "tab": [
     "pytorch"
    ],
    "id": "7bc7fa99",
    "outputId": "454a8e61-7ac4-439a-a4ab-8feeb12df5ea"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(5.)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u = torch.tensor([3.0, -4.0])\n",
    "torch.norm(u)"
   ],
   "id": "7bc7fa99"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 115,
    "id": "82eb55f9"
   },
   "source": [
    "在深度学习中，我们更经常地使用$L_2$范数的平方。\n",
    "你还会经常遇到[**$L_1$范数，它表示为向量元素的绝对值之和：**]\n",
    "\n",
    "(**$$\\|\\mathbf{x}\\|_1 = \\sum_{i=1}^n \\left|x_i \\right|.$$**)\n",
    "\n",
    "与$L_2$范数相比，$L_1$范数受异常值的影响较小。\n",
    "为了计算$L_1$范数，我们将绝对值函数和按元素求和组合起来。\n"
   ],
   "id": "82eb55f9"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.718921Z",
     "iopub.status.busy": "2022-07-31T02:49:25.718450Z",
     "iopub.status.idle": "2022-07-31T02:49:25.723592Z",
     "shell.execute_reply": "2022-07-31T02:49:25.722954Z"
    },
    "origin_pos": 117,
    "tab": [
     "pytorch"
    ],
    "id": "c63ec19d",
    "outputId": "83da1af4-3687-4cee-c3f4-1d7d25e61bdd"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(7.)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.abs(u).sum()"
   ],
   "id": "c63ec19d"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 119,
    "id": "b8772b63"
   },
   "source": [
    "$L_2$范数和$L_1$范数都是更一般的$L_p$范数的特例：\n",
    "\n",
    "$$\\|\\mathbf{x}\\|_p = \\left(\\sum_{i=1}^n \\left|x_i \\right|^p \\right)^{1/p}.$$\n",
    "\n",
    "类似于向量的$L_2$范数，[**矩阵**]$\\mathbf{X} \\in \\mathbb{R}^{m \\times n}$(**的*Frobenius范数*（Frobenius norm）是矩阵元素平方和的平方根：**)\n",
    "\n",
    "(**$$\\|\\mathbf{X}\\|_F = \\sqrt{\\sum_{i=1}^m \\sum_{j=1}^n x_{ij}^2}.$$**)\n",
    "\n",
    "Frobenius范数满足向量范数的所有性质，它就像是矩阵形向量的$L_2$范数。\n",
    "调用以下函数将计算矩阵的Frobenius范数。\n"
   ],
   "id": "b8772b63"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-07-31T02:49:25.726935Z",
     "iopub.status.busy": "2022-07-31T02:49:25.726429Z",
     "iopub.status.idle": "2022-07-31T02:49:25.731749Z",
     "shell.execute_reply": "2022-07-31T02:49:25.731145Z"
    },
    "origin_pos": 121,
    "tab": [
     "pytorch"
    ],
    "id": "2f5665d0",
    "outputId": "bc4d0b67-9497-42a0-9daa-6f2d08803539"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(6.)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.norm(torch.ones((4, 9)))"
   ],
   "id": "2f5665d0"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 123,
    "id": "b4affc09"
   },
   "source": [
    "### 范数和目标\n",
    ":label:`subsec_norms_and_objectives`\n",
    "\n",
    "在深度学习中，我们经常试图解决优化问题：\n",
    "*最大化*分配给观测数据的概率;\n",
    "*最小化*预测和真实观测之间的距离。\n",
    "用向量表示物品（如单词、产品或新闻文章），以便最小化相似项目之间的距离，最大化不同项目之间的距离。\n",
    "目标，或许是深度学习算法最重要的组成部分（除了数据），通常被表达为范数。\n",
    "\n",
    "## 关于线性代数的更多信息\n",
    "\n",
    "仅用一节，我们就教会了你所需的、用以理解现代深度学习的线性代数。\n",
    "线性代数还有很多，其中很多数学对于机器学习非常有用。\n",
    "例如，矩阵可以分解为因子，这些分解可以显示真实世界数据集中的低维结构。\n",
    "机器学习的整个子领域都侧重于使用矩阵分解及其向高阶张量的泛化，来发现数据集中的结构并解决预测问题。\n",
    "我们相信，一旦你开始动手尝试并在真实数据集上应用了有效的机器学习模型，你会更倾向于学习更多数学。\n",
    "因此，这一节到此结束，我们保留在后面介绍更多数学知识的权利。\n",
    "\n",
    "如果你渴望了解有关线性代数的更多信息，你可以参考[线性代数运算的在线附录](https://d2l.ai/chapter_appendix-mathematics-for-deep-learning/geometry-linear-algebraic-ops.html)或其他优秀资源 :cite:`Strang.1993,Kolter.2008,Petersen.Pedersen.ea.2008`。\n",
    "\n",
    "## 小结\n",
    "\n",
    "* 标量、向量、矩阵和张量是线性代数中的基本数学对象。\n",
    "* 向量泛化自标量，矩阵泛化自向量。\n",
    "* 标量、向量、矩阵和张量分别具有零、一、二和任意数量的轴。\n",
    "* 一个张量可以通过`sum`和`mean`沿指定的轴降低维度。\n",
    "* 两个矩阵的按元素乘法被称为他们的Hadamard积。它与矩阵乘法不同。\n",
    "* 在深度学习中，我们经常使用范数，如$L_1$范数、$L_2$范数和Frobenius范数。\n",
    "* 我们可以对标量、向量、矩阵和张量执行各种操作。\n",
    "\n",
    "## 练习\n",
    "\n",
    "1. 证明一个矩阵$\\mathbf{A}$的转置的转置是$\\mathbf{A}$，即$(\\mathbf{A}^\\top)^\\top = \\mathbf{A}$。\n",
    "1. 给出两个矩阵$\\mathbf{A}$和$\\mathbf{B}$，证明“它们转置的和”等于“它们和的转置”，即$\\mathbf{A}^\\top + \\mathbf{B}^\\top = (\\mathbf{A} + \\mathbf{B})^\\top$。\n",
    "1. 给定任意方阵$\\mathbf{A}$，$\\mathbf{A} + \\mathbf{A}^\\top$总是对称的吗?为什么?\n",
    "1. 我们在本节中定义了形状$(2,3,4)$的张量`X`。`len(X)`的输出结果是什么？\n",
    "1. 对于任意形状的张量`X`,`len(X)`是否总是对应于`X`特定轴的长度?这个轴是什么?\n",
    "1. 运行`A/A.sum(axis=1)`，看看会发生什么。你能分析原因吗？\n",
    "1. 考虑一个具有形状$(2,3,4)$的张量，在轴0、1、2上的求和输出是什么形状?\n",
    "1. 为`linalg.norm`函数提供3个或更多轴的张量，并观察其输出。对于任意形状的张量这个函数计算得到什么?\n"
   ],
   "id": "b4affc09"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 125,
    "tab": [
     "pytorch"
    ],
    "id": "2efd2d4e"
   },
   "source": [
    "[Discussions](https://discuss.d2l.ai/t/1751)\n"
   ],
   "id": "2efd2d4e"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "provenance": [
    {
     "file_id": "https://github.com/d2l-ai/d2l-zh-pytorch-colab/blob/master/chapter_preliminaries/ndarray.ipynb",
     "timestamp": 1664005675433
    }
   ],
   "toc_visible": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
